package edu.ustb.efws.base.service.sms;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.ustb.efws.EfwsProperties;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 发送短信
 *
 * @author TS Group
 */
public class SmsSender {
    private static final Logger LOGGER = LoggerFactory.getLogger(SmsSender.class);
    private static final Map<String, String> ERR_CODE_MSG = initErrMsg();
    private static final String SUCCESS_CODE = "0000";

    private final RestTemplate restTemplate;
    private final ObjectMapper objectMapper;
    private final EfwsProperties properties;

    /**
     * 发送短信
     *
     * @param restTemplate {@link RestTemplate}
     * @param objectMapper {@link ObjectMapper}
     * @param properties {@link EfwsProperties}
     */
    public SmsSender(RestTemplate restTemplate, ObjectMapper objectMapper, EfwsProperties properties) {
        this.restTemplate = restTemplate;
        this.objectMapper = objectMapper;
        this.properties = properties;
    }

    /**
     * 发送短信
     *
     * @param ordId 订单编号
     * @param params 发送参数
     * @return {@link SmsResult}
     */
    public SmsResult SmsResult(String ordId,  String mobile, Map<String, String> params){

        try{
            RequestEntity<byte[]> requestEntity = buildRequest(ordId, mobile, params);

            ResponseEntity<byte[]> response = restTemplate.exchange(requestEntity, byte[].class);

            if(response.getBody() == null){
                return new SmsResult("-1", "返回数据为空", false);
            }

            String content = new String(response.getBody(), StandardCharsets.UTF_8);
            LOGGER.debug("Sms response body={}", content);

            JsonNode jsonNode = objectMapper.readTree(content);

            String code = jsonNode.get("return_code").asText("-1");
            boolean success = StringUtils.equals(code, SUCCESS_CODE);
            String errMsg = success? "": ERR_CODE_MSG.getOrDefault(code, "系统异常");

            return new SmsResult(code, errMsg, success);
        }catch (Exception e){
            LOGGER.error("Send sms fail mobile={}, error={}", mobile, e.getMessage());
            return new SmsResult("-2", "请求网络错误", false);
        }
    }

    /**
     * 构建请求 Request
     *
     * @param ordId 订单编号
     * @param mobile 手机号
     * @param params 参数
     * @return {@link RequestEntity}
     */
    private RequestEntity<byte[]> buildRequest(String ordId, String mobile, Map<String, String> params){
        String body = buildBody(ordId, mobile, params);
        LOGGER.debug("Sms body={}", body);

        return RequestEntity
                .post(URI.create(properties.getSmsUrl()))
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .acceptCharset(StandardCharsets.UTF_8)
                .body(body.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 构建发送body
     *
     * @param ordId 订单编号
     * @param mobile 手机号
     * @param params 参数
     * @return 请求body
     */
    private String buildBody(String ordId, String mobile, Map<String, String> params){
        String paramsStr = params.entrySet().stream()
                .map(e -> e.getKey() + ":" + e.getValue())
                .collect(Collectors.joining(","));

        long time = Instant.now().toEpochMilli();
        String sign = sign(mobile, time);

        return String.format("account=%s&mobile=%s&order_id=%s&time=%s&tpl_id=%s&params=%s&sign=%s",
                properties.getSmsAccount(), mobile, ordId, time, properties.getSmsTemplate(), paramsStr, sign);
    }

    /**
     * 生成签名
     *
     * mobile|account|time|tpl_id#tradeKey,加密字符串规则
     * tradeKey 是由您申请的交易密钥
     *
     * @param mobile 手机号
     * @param time 签名时间
     *
     * @return 签名
     */
    private String sign(String mobile, long time) {
        String source = mobile + "|" + properties.getSmsAccount() +
                "|" + time + "|" + properties.getSmsTemplate() + "#" + properties.getSmsKey();
        return md5(source).toUpperCase();
    }

    /**
     * md5签名
     *
     * @param plainText 需要加密的字符串
     * @return 签名字符串
     */
    private String md5(String plainText) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.update(plainText.trim().getBytes(StandardCharsets.UTF_8));
            byte[] hashBytes = messageDigest.digest();
            int i;
            StringBuilder mdStr = new StringBuilder(32);
            for (byte hashByte : hashBytes) {
                i = hashByte;
                if (i < 0) {
                    i += 256;
                }
                if (i < 16) {
                    mdStr.append("0");
                }
                mdStr.append(Integer.toHexString(i));
            }
            return mdStr.toString().toUpperCase();
        } catch (Exception e) {
            LOGGER.error("Sms md5 fail plainText={}, error={}", plainText, e.getMessage());
        }

        return "";
    }

    private static Map<String, String> initErrMsg(){
        Map<String, String> errors = new HashMap<>();
        errors.put("0000","请求成功");
        errors.put("4598","手机号存在在黑名单中");
        errors.put("5002","手机号码无效");
        errors.put("5003","订单号重复");
        errors.put("5004","商户不存在");
        errors.put("5006","商户账户不存在");
        errors.put("5007","商户账号余额不足");
        errors.put("5016","同一号码相同内容30秒内只能获取一次");
        errors.put("9999","系统异常");

        return errors;
    }
}
